/**
 * @file    app_scene.c
 * @author  Azolla (1228449928@qq.com)
 * @brief   网关场景联动
 * @version v0.1
 * @date    2023-02-28
 * 
 * @copyright Copyright (c) 2023
 * */
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"

#include "cJSON.h"

#include "wifi_mesh.h"
#include "wifi_sntp.h"

// #define CONFIG_FATFS_FALSH     1        
#define CONFIG_FATFS_SD_TFT    1      

#ifdef CONFIG_FATFS_FALSH
#include "app_spiffs.h"
#endif
#ifdef CONFIG_FATFS_SD_TFT
#include "bsp_sdspi.h"
#endif

#include "app_scene.h"

#define TAG  "app_scene"        // 注释掉，可关闭DEBUG信息

#ifdef CONFIG_FATFS_SD_TFT
#define SCENE_ROOT_PATH   MOUNT_POINT"/SCENE"      // 情景根目录路径
#endif

#ifdef CONFIG_FATFS_FALSH
#define SCENE_ROOT_PATH   "/spiffs"      // 情景根目录路径
#endif
 
static bool scene_update_flag = true;   // ture: 情景有写/删除的更新。

// return: ture情景更新事件
// 如果情景用更新，需要重新读条件触发条件！
bool scene_update_event(void)
{
    bool event = scene_update_flag;
    scene_update_flag = 0;
    return event;
}

// 写情景 
void scene_write(const char *name, const char *ftext)
{
    char fpath[24]; // "/spiffs/1"
    sprintf(fpath, ""SCENE_ROOT_PATH"/%s", name); 
    #ifdef TAG
    ESP_LOGI(TAG, "write fpath: %s \n%s", fpath, ftext);
    #endif
    FILE *fb = fopen(fpath, "w+");  // 重新写入
    fwrite(ftext, sizeof(char), strlen(ftext), fb);
    fclose(fb);  // Remember to close file.
    scene_update_flag = true;   // 更新
} 

// 读情景
int scene_read(const char *name, char *ftext)
{
    int fsize = 0;
    char fpath[24]; 
    sprintf(fpath, ""SCENE_ROOT_PATH"/%s", name); 
    #ifdef TAG
    ESP_LOGI(TAG, "read fpath: %s", fpath);
    #endif
    FILE *fb = fopen(fpath, "r");
    if (fb != NULL) {
        fsize = fread(ftext, sizeof(char), 1024, fb);
        fclose(fb);  // Remember to close file.
    }
    return fsize;
} 

static int scene_remove(const char *name)
{
    char fpath[24];  
    sprintf(fpath, ""SCENE_ROOT_PATH"/%s", name); 
    #ifdef TAG
    ESP_LOGI(TAG, "delete fpath: %s", fpath);
    #endif
    scene_update_flag = true;   // 更新
    return remove(fpath);  // 删除文件
}

// 删除情景
int scene_delete(const char *name)
{
    if (strncmp(name, "all", 3) == 0) {  // "all"全删

        DIR *dir = NULL;  
        struct dirent *entry;  
        
        if ( (dir = opendir(SCENE_ROOT_PATH)) == NULL){  
            return 0;
        }

        while ( (entry = readdir(dir)) != NULL) {  
            if (entry->d_type != DT_REG) continue;
            char fname[4];
            strcpy(fname, entry->d_name);
            scene_remove(fname);
        }  
        closedir(dir);   
        return 0;  
    }  

    return scene_remove(name);
}
 
// 读触发情景事件
bool scene_read_tigger(scene_trigger_t *trigger)
{
    DIR *dir = NULL;  
    struct dirent *entry;     
    if ( (dir = opendir(SCENE_ROOT_PATH)) == NULL) {  
        #ifdef TAG
        ESP_LOGE(TAG, "opendir failed!");  
        #endif
        return 0;  
    }

    char *ftext = heap_caps_malloc(2 * 1024, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    assert(ftext);

    memset(trigger, 0, sizeof(scene_trigger_t));  // 记得先清零
 
    uint8_t findex = 0;  // 文件索引
    while ( (entry = readdir(dir)) != NULL ) {  
        #ifdef TAG  // 输出文件或者目录的名称, 文件类型   
        printf("scene_name[%d] = %s | %d\r\n", findex++, entry->d_name, entry->d_type); 
        #endif 
        if (entry->d_type != DT_REG) continue;  // 不是文件
        int size = scene_read(entry->d_name, ftext);
        if (size > 0) {
            ftext[size] = '\0';  // end mark.
            #ifdef TAG  // debug
            ESP_LOGI(TAG, "read->ftext: %s | %d", ftext, size);
            #endif
            cJSON *jroot = cJSON_Parse((char *)ftext);    
            if (jroot == NULL) {  // ERROR!!!
                #ifdef TAG  // debug
                ESP_LOGE(TAG, "scene_read_tigger cJSON_Parse error");
                #endif
                continue;
            }
            cJSON *trigger_item = cJSON_GetObjectItem(jroot, "trigger");
            if (trigger_item == NULL) {  // ERROR!!!
                #ifdef TAG  // debug
                ESP_LOGE(TAG, "cJSON trigger error");
                #endif
                cJSON_Delete(jroot);
                continue;
            }
    
            for (uint8_t index = 0; index < cJSON_GetArraySize(trigger_item); index++) {
                cJSON *item = cJSON_GetArrayItem(trigger_item, index);
                if (cJSON_IsNull(item))  break; // 为空
                if (item->type != cJSON_Object) break;  
                strcpy(trigger->event[trigger->total].name, entry->d_name);  // 得到触发情景名
                cJSON *timer_item = cJSON_GetObjectItem(item, "timer");
                if (timer_item != NULL) {  // 是定时事件
                    if (timer_item->type != cJSON_Array) continue;
                    for (uint8_t index = 0; index < cJSON_GetArraySize(timer_item); index++) {
                        cJSON *item = cJSON_GetArrayItem(timer_item, index);
                        if (cJSON_IsNull(item))  break; // 为空
                        if (index == 0) {
                            trigger->event[trigger->total].week = item->valueint;  // 星期
                        } else if (index == 1) {
                            trigger->event[trigger->total].time = item->valueint;  // 时间
                        } else {
                            trigger->event[trigger->total].dtime = item->valueint;  // 持续时间
                        }
                    }
                }

                cJSON *addr_item = cJSON_GetObjectItem(item, "addr");
                if (addr_item != NULL) {  // 是设备事件
                    if (addr_item->type != cJSON_String) continue;
                    strcpy(trigger->event[trigger->total].name, entry->d_name);  // 得到触发情景名
                    mlink_mac_str2hex(addr_item->valuestring, trigger->event[trigger->total].addr); // 设备地址
                    cJSON_DeleteItemFromObject(item, "addr"); // 删除"addr"节点
                    char *data = cJSON_PrintUnformatted(item);
                    strcpy(trigger->event[trigger->total].instruct, data); 
                    #ifdef TAG  // debug
                    ESP_LOGI(TAG, "instruct = %s", data);
                    #endif
                    cJSON_free(data);
                } 

                trigger->total++;  // 触发事件++
            }
            cJSON_Delete(jroot);
        }
    }  
    closedir(dir);     
    free(ftext);
    return true;
} 

extern SemaphoreHandle_t xSemap; 
extern uint8_t app_parse_handler(app_parse_data_t parse, cJSON *item);

// 执行情景动作 
// name: 情景名
// action: 情景动作："start/stop"
int app_scene_action(const char *name, const char *action)
{
    // ble_bind_device_update();
    char *ftext = heap_caps_malloc(1024, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    assert(ftext);  assert(action);  assert(name);  
    int size = scene_read(name, ftext);
    if (size > 0) {
        ftext[size] = '\0';  // end mark.
        #ifdef TAG  // debug
        ESP_LOGI(TAG, "action->ftext: %s | %d", ftext, size);
        #endif
        cJSON *jroot = cJSON_Parse((char *)ftext);    
        if (jroot == NULL) {  // ERROR!!!
            #ifdef TAG  // debug
            ESP_LOGE(TAG, "scene_read_tigger cJSON_Parse error");
            #endif
            free(ftext);
            return -1;
        }

        cJSON *action_item = cJSON_GetObjectItem(jroot, action);
        if (action_item == NULL) {
            #ifdef TAG  // debug
            ESP_LOGE(TAG, "action_item error");
            #endif
            cJSON_Delete(jroot);
            free(ftext);
            return -2;
        }
        uint8_t jSize = cJSON_GetArraySize(action_item);
        for (uint8_t size = 0; size < jSize; size++) {
            #ifdef TAG 
            ESP_LOGW(TAG, "cJSON_GetArraySize = %d", size);
            #endif
            cJSON *arr_item = cJSON_GetArrayItem(action_item, size);  // 遍历
            if (cJSON_IsNull(arr_item))  break; // 为空
            if (arr_item->type != cJSON_Object) continue;
            #ifdef TAG 
            char *data = cJSON_PrintUnformatted(arr_item);
            ESP_LOGI(TAG, "action->%s = %s", action, data);
            cJSON_free(data);
            #endif
            //===========================================================================
            app_parse_data_t parse = { 0 };
            cJSON *addr_item = cJSON_GetObjectItem(arr_item, "addr");
            if (addr_item != NULL) {
                mlink_mac_str2hex(addr_item->valuestring, parse.addr); // 设备地址
                cJSON_DeleteItemFromObject(arr_item, "addr"); // 删除"addr"节点
                parse.json = arr_item;  // 记住当前的JSON原始数据
            }  
            /* ***************************** 功能指令集解析 ************************* */
            for (uint8_t index = 0; (parse.json != NULL && index < cJSON_GetArraySize(parse.json)); index++) {
                cJSON *item = cJSON_GetArrayItem(parse.json, index);  // 遍历
                if (cJSON_IsNull(item))  break; // 为空
                 if (xSemaphoreTake(xSemap, portMAX_DELAY) == pdTRUE) {  // 等待获取互斥信号量
                    parse.is_scene = true;   // 情景解析
                    int parse_ret = app_parse_handler(parse, item); // 解析设备指令集
                    xSemaphoreGive(xSemap);  // 释放互斥信号量
                    if (parse_ret == APP_PARSE_EXIT || parse_ret == APP_PARSE_NULL)  break;   
                }
            }
        }
        cJSON_Delete(jroot);
    }
    free(ftext);
    return 0;
}


 
void app_scene_init(void)
{
#ifdef CONFIG_FATFS_FALSH    
    app_spiffs_init();
#endif    

#if 0    
    timer_trigger_t timer = { 0 };
    device_trigger_t device = { 0 };
    // clear zero...
    // memset(&timer,  0, sizeof(timer_trigger_t));
    // memset(&device, 0, sizeof(device_trigger_t));
    // 如果情景有更新，需要重新读条件触发条件！
    if ( scene_update_event() == true ) {
        scene_read_tigger(&timer, &device);
    }
 
    /* 情景之定时器触发 */
    for (uint8_t i = 0; i < timer.total; i++) {
        #ifdef TAG
        ESP_LOGI(TAG, "timer.event[%d].name = %s", i, timer.event[i].name);
        ESP_LOGI(TAG, "timer.event[%d].week = 0x%02x", i, timer.event[i].week);
        ESP_LOGI(TAG, "timer.event[%d].time = %d", i, timer.event[i].time);
        #endif
        struct tm *sntp = sntp_get_time();
        if (timer.event[i].week & (1 << sntp->tm_wday)) { // 先判断星期
            if (timer.event[i].time == sntp->tm_hour * 24 + sntp->tm_min) { // 24H制一天的分钟
                if (sntp->tm_sec == 0) { // 时间到了
                    // 触发情景
                }
            }
        }
    }

    /* 情景之设备状态触发 */
    for (uint8_t i = 0; i < device.total; i++) {
        #ifdef TAG
        ESP_LOGI(TAG, "device.event[%d].name = %s", i, device.event[i].name);
        ESP_LOGI(TAG, "device.event[%d].addr = "MACSTR"", i, MAC2STR(device.event[i].addr));
        ESP_LOGI(TAG, "device.event[%d].instruct = %s", i, device.event[i].instruct);
        #endif
        // if (memcmp(device.event[i].addr, addr, 6) == 0) {  // 先对比地址
        //     if (strcmp(device.event[i].instruct, data, len) == 0) { // 再对比指令字段
        //         // 触发情景
        //     }
        // }
    }

    app_scene_action(device.event[0].name);
#endif    
   
#if 0  // test...
    scene_write("1", "{\"addr\":\"94b555311dee\",\"onoff\":\"1\",\"level\":\"1\"}");
    char ftext[256];
    while (true) {
        int size = scene_read("1", ftext);
        if (size > 0) {
            ftext[size] = '\0';
            ESP_LOGI(TAG, "ftext: %s | %d", ftext, size);
        }
        vTaskDelay(3000);
    }
#endif    
}


